/**
 * 2017年11月23日
 */
package exp.algorithm.sic.weight;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;

import exp.algorithm.sic.InstanceFeatureVectors;
import exp.algorithm.sic.feature.FeatureVector;
import weka.core.Attribute;

/**
 * @author Alex
 *
 */
public class ReliefFWeight implements Serializable {
	private static final long serialVersionUID = -3005596966131737678L;
	Logger log = org.slf4j.LoggerFactory.getLogger(ReliefFWeight.class);
	int topK = 5;
	int totalVecotor = -1;
	interface FVCB{
		void doWhat(FeatureVector fv,double classVal,int seq,int totalSeq);
	}
	static class FeatureVectorHolder{
		public int globalIndex;
		//第i个实例
		public int i;
		//在第i个实例中的向量索引
		public int j;
		public FeatureVector fv;
		public double classValue;
		public FeatureVectorHolder(FeatureVector fv,int globalIndex,int i ,int j,double classValue) {
			this.i = i;
			this.j = j;
			this.fv = fv;
			this.globalIndex = globalIndex;
			this.classValue = classValue;
		}
		public String toString(){
			return "{globalIndex:"+globalIndex+" InstNo.: "+i+" VecNo.: "+j+" ClassValue: "+classValue+"}";
		}
	}
	double [] weights;
	int []classValues ;
	double dist[][];
	int count[];
	public double [] getWeights(){
		return weights;
	}
	public int[] getCorrespodingClassValue(){
		return classValues;
	}
	
	//classFeatureVal是类标数目
	public void input(List<InstanceFeatureVectors> vecs,int classAttNum,Attribute classAttribute,double[]prioris){
		this.count = new int[classAttNum];
		int k =0;
		for(int i =0;i<vecs.size();i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			count[(int)ifv.classValue] += ifv.vecs.size();
			k+=ifv.vecs.size();
		}
		totalVecotor = k;
		//共k个特征向量
		this.weights= new double[k];
		log.error("Total Vectors : {}" , k);
		this.classValues = new int[weights.length];
		int instNum = vecs.size();
		//这是一个上三角阵,下三角没用
		this.dist = new double[k][k];
//		for(int i=0;i<k;i++){
//			dist[i] = new double[k-i];
//		}
		//利用这个indexList计算dist矩阵的值,dist[i][j]表示indexList中的第i个与第j个的距离是dist[i][j];
		List<FeatureVectorHolder> indexList = new ArrayList<FeatureVectorHolder>(k);
		int sq  = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				//该向量是属性训练集中第i个实例中的第j个向量,第i个实例的类标值是classValue
				indexList.add(new FeatureVectorHolder(ifv.vecs.get(j),sq, i, j, classValue));
				classValues[sq] = (int)classValue;
				sq++;
			}
		}
		for(int i = 0;i<k;i++){
			setDist(i, i, 0.0);
			for(int j = i+1;j<k;j++){
				setDist(i, j, distance(indexList.get(i),indexList.get(j)));
			}
		}
		sq  = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				FeatureVector fv = ifv.vecs.get(j);
				List<DistanceHolder> kHits = findKHit(fv,sq,(int)classValue,indexList);
				List<DistanceHolder> kMisses = findKMiss(fv,sq,(int)classValue,indexList);
				double weight =  calcWeight(fv, sq, kHits, kMisses, prioris);
				this.weights[sq] = weight;
				sq++;
			}
		}
	}
	double getDist(int i,int j){
		if(i==j)return dist[i][i];
		else if(i>j) {
			return dist[j][totalVecotor-i];
		}else{
			return dist[i][totalVecotor - j];
		}
	}
	void setDist(int i,int j,double val){
		if(i==j) dist[i][i] =val;
		else if(i>j) {
			dist[j][totalVecotor-i] = val;
		}else{
			dist[i][totalVecotor - j] = val;
		}
	}
	
	private double calcWeight(FeatureVector fv,int fvIndex,List<DistanceHolder> kHits,List<DistanceHolder> kMisses,double[]prioris){
		int actualHitNum = kHits.size();
		double hitWeight = 0.0;
		
		for(int i = 0 ; i<actualHitNum ;i++){
			DistanceHolder dh = kHits.get(i);
			hitWeight -= getDist(fvIndex,dh.index);
		}
		if(actualHitNum!=0)
			hitWeight /= actualHitNum;
		int actualMissNum = kMisses.size();
		double missWeight = 0.0;
		for(int i=0 ; i < actualMissNum ;i ++){
			DistanceHolder dh = kMisses.get(i);
			missWeight += getDist(fvIndex,dh.index);
		}
		if(actualMissNum!=0)
			missWeight /= actualMissNum;
		
		return (hitWeight + missWeight);
	}
	
	private List<DistanceHolder> findK(FeatureVector vector,int index, int classValue,List<FeatureVectorHolder> indexList,boolean hit){
		List<DistanceHolder> result = new ArrayList<>(count[classValue]);
		for(int i = 0;i<classValues.length;i++){
			if( i == index )continue; //排除自己
			if(hit){
				if(classValues[i] != classValues[index]) continue; //因为是Hit排除不同类
			}else{
				if(classValues[i] == classValues[index]) continue; //因为是Misst排除同类
			}
			result.add(new DistanceHolder(i, getDist(index,i)));
		}
		result.sort((x,y)->{
			return Double.compare(x.distance, y.distance);
		});
		return result.subList(0, Math.min(topK, result.size()));
	}
	
	private List<DistanceHolder> findKHit(FeatureVector vector,int index, int classValue,List<FeatureVectorHolder> indexList){
		return findK(vector, index, classValue, indexList, true);
	}
	private List<DistanceHolder> findKMiss(FeatureVector vector,int index, int classValue,List<FeatureVectorHolder> indexList){
		return findK(vector, index, classValue, indexList, false);
	}
	
	static class DistanceHolder{
		public int index;
		public double distance;
		public DistanceHolder(int i,double distance) {
			this.index = i;
			this.distance = distance;
		}
		public String toString(){
			return "{index:"+index+" distance:"+distance+"}";
		}
	}
	
	private double distance(FeatureVectorHolder fvh1, FeatureVectorHolder fvh2){
		return distance(fvh1.fv, fvh2.fv);
	}
	
	private void iteratorFeatrueVector(List<InstanceFeatureVectors> vecs,FVCB fvcb){
		int instNum = vecs.size();
		int seq = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				FeatureVector fv = ifv.vecs.get(j);
				fvcb.doWhat(fv, classValue, j, seq);
				seq ++ ;
			}
		}
	}
	
	
	private double distance(FeatureVector v1,FeatureVector v2){
		double sum = 0.0;
		for(int i = 0 ;i<v1.length();i++){
			if(!Double.isNaN(v1.getVal(i)) && !Double.isNaN(v2.getVal(i)))
				sum = sum + Math.sqrt(Math.abs(v1.getVal(i)*v1.getVal(i)-v2.getVal(i)*v2.getVal(i)));
		}
		return Math.sqrt(sum);
	}
	
	
}
